package com.pms.utils;

import com.baidubce.BceClientException;
import com.baidubce.util.DateUtils;
import com.pms.exception.RRException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Header;
import org.apache.http.client.methods.HttpRequestBase;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.*;

/**
 * Created by Administrator on 2017/12/27.
 */
//@Component
public class AuthorizationUtil {
    @Value("${baidu.AccessKey}")
    private String ACCESS_KEY_ID;
    @Value("${baidu.SecretKey}")
    private String SECRET_ACCESS_KEY;

    private static final Integer DEFAULT_VERSION = 1;// 默认签名版本号
    private static final Integer DEFAULT_EXPIRETIME = 1800; //默认过期时间为1800秒
    private static final String[] DEFAULT_CANONICAL_HEADER_ARR ={"Host","x-bce-date","Content-Type"};// 进行编码的header数组
//    public static void main(String arg[]) throws Exception {
//    }
    public String generate_AuthorizationPrefix() {
       return generate_AuthorizationPrefix(DEFAULT_EXPIRETIME);
    }
    /**
     * 生成认证字符串
     * @param req 请求头
     * @param paramMap 请求头参数Map
     * @return
     */
    public String generate_Authorization(HttpRequestBase req,Map<String,String> paramMap){
        String authorizationPrefix = generate_AuthorizationPrefix();// 认证字符串前缀
        return authorizationPrefix+"/"+generate_SignedHeadersBy_DEFAULT()+"/"
                + generate_Signature(generate_SigningKey(authorizationPrefix),generate_CanonicalRequest(req,paramMap));
    }
    /**
     * 生成认证字符串
     * @param expireTime 签名有效时间
     * @param req 请求头
     * @param paramMap 请求头参数Map
     * @return
     */
    public String generate_Authorization(Integer expireTime,HttpRequestBase req,Map<String,String> paramMap){
        String authorizationPrefix = generate_AuthorizationPrefix(expireTime);
        return authorizationPrefix+"/"+generate_SignedHeadersBy_DEFAULT()+"/"+generate_Signature(generate_SigningKey(authorizationPrefix)
                ,generate_CanonicalRequest(req,paramMap));
    }
    /**
     * 生成认证字符串
     * @param authorizationPrefix 前缀
     * @param req 请求头
     * @param paramMap 请求头参数Map
     * @return
     */
    public String generate_Authorization(String authorizationPrefix,HttpRequestBase req,Map<String,String> paramMap){
        return authorizationPrefix+"/"+generate_SignedHeadersBy_DEFAULT()+"/"+generate_Signature(generate_SigningKey(authorizationPrefix)
                ,generate_CanonicalRequest(req,paramMap));
    }
    /**
     * 生成认证字符串
     * @param authorizationPrefix 前缀
     * @param SignedHeaders
     * @param req 请求头
     * @param paramMap 请求头参数Map
     * @return
     */
    public String generate_Authorization(String authorizationPrefix,String SignedHeaders,HttpRequestBase req,Map<String,String> paramMap){
        return authorizationPrefix+"/"+SignedHeaders+"/"+generate_Signature(generate_SigningKey(authorizationPrefix)
                ,generate_CanonicalRequest(req,paramMap));
    }

    /**
     * 生成认证字符串
     * @param authorizationPrefix 前缀
     * @param SignedHeaders
     * @param canonicalRequest
     * @return
     */
    public String generate_Authorization(String authorizationPrefix,String SignedHeaders,String canonicalRequest){
        return authorizationPrefix+"/"+SignedHeaders+"/"+generate_Signature(generate_SigningKey(authorizationPrefix),canonicalRequest);
    }
    /**
     * 生成认证字符串
     * @param authorizationPrefix 前缀
     * @param SignedHeaders
     * @param SigningKey
     * @param canonicalRequest
     * @return
     */
    public String generate_Authorization(String authorizationPrefix,String SignedHeaders,String SigningKey,String canonicalRequest){
        return authorizationPrefix+"/"+SignedHeaders+"/"+generate_Signature(SigningKey,canonicalRequest);
    }

    /**
     * 生成 认证字符串前缀
     *
     * @param expireTime 签名有效时间单位为秒
     * @return bce-auth-v{version}/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds}
     */
    public String generate_AuthorizationPrefix(Integer expireTime) {
        if (expireTime == null) {
            expireTime = DEFAULT_EXPIRETIME;
        }
        StringBuilder authorizationPrefix = new StringBuilder("bce-auth-v");
        //设置版本 {version} version是正整数，目前取值为1。
        authorizationPrefix.append(DEFAULT_VERSION);
        // 设置ACCESS_KEY_ID  /{accessKeyId}
        authorizationPrefix.append("/").append(ACCESS_KEY_ID);
        // 设置请求时间 /{timestamp}
        authorizationPrefix.append("/").append(DateUtils.formatAlternateIso8601Date(new Date()));
        // 设置签名有效时间/{expireTime}  单位为秒，从timestamp所指定的时间开始计算。
        authorizationPrefix.append("/").append(expireTime);//设置签名有效时间,默认为10秒
        return authorizationPrefix.toString();
    }
    /**
     * 生成 CanonicalRequest
     * @param req 请求头
     * @param paramMap 请求参数map
     * @return
     */
    public String generate_CanonicalRequest(HttpRequestBase req,Map<String,String> paramMap){
        String httpMethod = req.getMethod();
//        String canonicalURI = req.getURI().getPath();// 不确定 uriEncodeExceptSlash(path) 方法实现
        String canonicalURI = uriEncodeExceptSlash(req.getURI().getPath());
        String canonicalQueryStr = generate_CanonicalQueryStr(paramMap,true);
        String canonicalHeaders = generate_CanonicalHeadersBy_DEFAULT(req);
        return generate_CanonicalRequest(httpMethod,canonicalURI,canonicalQueryStr,canonicalHeaders);
    }
    /**
     * 生成 CanonicalRequest
     * @param req 请求头
     * @param canonicalQueryStr
     * @param canonicalHeaders
     * @return
     */
    public String generate_CanonicalRequest(HttpRequestBase req,String canonicalQueryStr,String canonicalHeaders){
        String httpMethod = req.getMethod();
//        String canonicalURI = req.getURI().getPath();// 不确定 uriEncodeExceptSlash(path) 方法实现
        String canonicalURI = uriEncodeExceptSlash(req.getURI().getPath());
        return generate_CanonicalRequest(httpMethod,canonicalURI,canonicalQueryStr,canonicalHeaders);
    }
    /**
     * 生成 SigningKey
     * @param expireTime
     * @return
     */
    public String generate_SigningKey(Integer expireTime){
        //HMAC-SHA256-HEX(SK,authorizationPrefix)
        return HMAC_SHA256_HEX(SECRET_ACCESS_KEY,generate_AuthorizationPrefix(expireTime));
    }
    public String generate_SigningKey(){
        //HMAC-SHA256-HEX(SK,authorizationPrefix)
        return HMAC_SHA256_HEX(SECRET_ACCESS_KEY,generate_AuthorizationPrefix(DEFAULT_EXPIRETIME));
    }

    public String generate_SigningKey(String authorizationPrefix){
        //HMAC-SHA256-HEX(SK,authorizationPrefix)
        return HMAC_SHA256_HEX(SECRET_ACCESS_KEY,authorizationPrefix);
    }
    /**
     * 生成签名
     * @param SigningKey
     * @param canonicalRequest
     * @return
     */
    public String generate_Signature(String SigningKey,String canonicalRequest){
        //HMAC-SHA256-HEX(SigningKey,canonicalRequest)
        return  HMAC_SHA256_HEX(SigningKey,canonicalRequest);
    }


    /**
     * 生成 CanonicalRequest
     * @param httpMethod GET,POST,DELETE,PUT,HEAD
     * @param canonicalURI eg: /v3/**
     * @param canonicalQueryStr
     * @param canonicalHeaders
     * @return
     */
    public String generate_CanonicalRequest(String httpMethod,String canonicalURI,String canonicalQueryStr,String canonicalHeaders){
        return httpMethod+"\n"+canonicalURI+"\n"+canonicalQueryStr+"\n"+canonicalHeaders;
    }

    /**
     * 生成 CanonicalQueryStr 根据key值排序
     * @param paramMap 参数map结合
     * @param isForCanonicalQuery true生成CanonicalQueryStr,false生成请求参数字符串
     * @return
     */
    public String generate_CanonicalQueryStr(Map<String,String> paramMap,boolean isForCanonicalQuery) {
        if (paramMap == null || paramMap.isEmpty()) {
            return "";
        }
        StringBuilder canonicalQueryStr = null;
        if(isForCanonicalQuery){
            List<String> paramStrList = new ArrayList<String>();
            Map<String,Object> paramQueryStrMap = new HashMap<String, Object>();
            //遍历map中的键
            for (String key : paramMap.keySet()) {
                String value = paramMap.get(key);
                paramStrList.add(uriEncode(key) + "=");
                if(value==null){
                    paramQueryStrMap.put(uriEncode(key) + "=",uriEncode(key) + "=");
                }
                if(value!=null){
                    paramQueryStrMap.put(uriEncode(key) + "=",uriEncode(key) + "="+uriEncode(value));
                }
            }
            Collections.sort(paramStrList);// 按照字典序进行排序
            for(int x=0;x<paramStrList.size();x++){
                if(canonicalQueryStr!=null){
                    canonicalQueryStr.append("&");
                }
                if(canonicalQueryStr==null){
                    canonicalQueryStr = new StringBuilder("");
                }
//                canonicalQueryStr.append(paramStrList.get(x));
                canonicalQueryStr.append(paramQueryStrMap.get(paramStrList.get(x)));
            }
        }
        if(!isForCanonicalQuery){
            for (String key : paramMap.keySet()) {
                if(canonicalQueryStr!=null){
                    canonicalQueryStr.append("&");
                }
                if(canonicalQueryStr==null){
                    canonicalQueryStr = new StringBuilder("");
                }
                canonicalQueryStr.append(key).append("=").append(paramMap.get(key));
            }
        }
        if(canonicalQueryStr==null){
            canonicalQueryStr = new StringBuilder("");
        }
        return canonicalQueryStr.toString();
    }

    /**
     * 生成 signedHeaders，根据 DEFAULT_CANONICAL_HEADER_ARR生成
     * @return
     */
    public String generate_SignedHeadersBy_DEFAULT(){
        if(DEFAULT_CANONICAL_HEADER_ARR.length<1){
            throw  new RRException("header中Host不能为空");
        }
        List<String> headerKeys = new ArrayList<String>();
        for(int x=0;x<DEFAULT_CANONICAL_HEADER_ARR.length;x++){
            headerKeys.add(DEFAULT_CANONICAL_HEADER_ARR[x].toLowerCase());//转小写
        }
        Collections.sort(headerKeys);//排序
        StringBuilder signedHeader = null;
        for(int x=0;x<headerKeys.size();x++){
            if(signedHeader!=null){
                signedHeader.append(";");
            }
            if(signedHeader==null){
                signedHeader = new StringBuilder("");
            }
            signedHeader.append(headerKeys.get(x).trim());
        }
        return signedHeader.toString();
    }
    /**
     * 对HTTP请求中的Header部分进行选择性编码,Host域必须被编码
     * 默认只编码 Host域 和 x-bce-date
     * @return
     */
     public String generate_CanonicalHeadersBy_DEFAULT(HttpRequestBase req){
         String canonicalHeaderStr = null;
        StringBuilder canonicalHeaders = null;
        List<String> headers = new ArrayList<String>();
        if(DEFAULT_CANONICAL_HEADER_ARR.length<1){
           Header[] headerArr = req.getAllHeaders();
           for(int x=0;x<headerArr.length;x++){
               String name = headerArr[x].getName().toLowerCase();//转小写
               String value =headerArr[x].getValue().trim();//取值去空格
               headers.add(uriEncode(name)+":"+uriEncode(value));
//               headers.add(name+":"+value);
           }
        }
        if(DEFAULT_CANONICAL_HEADER_ARR.length>0){
            for(int x=0;x<DEFAULT_CANONICAL_HEADER_ARR.length;x++){
                String name = DEFAULT_CANONICAL_HEADER_ARR[x].toLowerCase();//转小写
                String value = req.getHeaders(DEFAULT_CANONICAL_HEADER_ARR[x])[0].getValue().trim();//取值去空格
                if(StringUtils.isBlank(value)){
                    throw  new RRException("header中"+DEFAULT_CANONICAL_HEADER_ARR[x]+"的值不能为空");
                }
                headers.add(uriEncode(name)+":"+uriEncode(value));
//                headers.add(name+":"+value);
            }
        }
        Collections.sort(headers);
         for(int x=0;x<headers.size();x++){
             if(canonicalHeaders!=null){
                 canonicalHeaders.append("\n");
             }
             if(canonicalHeaders==null){
                 canonicalHeaders = new StringBuilder("");
             }
             canonicalHeaders.append(headers.get(x));
         }
        return canonicalHeaders.toString();
    }

    //"斜杠（/）做编码处理"
    public static String uriEncode(CharSequence input) {
        return uriEncode(input,true);
    }
    //"斜杠（/）不做编码处理"
    public static String uriEncodeExceptSlash(CharSequence input) {
        return uriEncode(input,false);
    }

    /**
     * 对字符串做编码处理
     * RFC 3986规定，"URI非保留字符"包括以下字符：字母（A-Z，a-z）、数字（0-9）、连字号（-）、点号（.）、下划线（_)、波浪线（~），算法实现如下：
     * 1. 将字符串转换成UTF-8编码的字节流
     * 2. 保留所有“URI非保留字符”原样不变
     * 3. 对其余字节做一次RFC 3986中规定的百分号编码（Percent-encoding），即一个“%”后面跟着两个表示该字节值的十六进制字母，字母一律采用大写形式。
     * @param input  字符串
     * @param encodeSlash 字符串中的 斜杠（/）做不做编码"
     * @return
     */
    public static String uriEncode(CharSequence input, boolean encodeSlash) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < input.length(); i++) {
            char ch = input.charAt(i);
            if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-' || ch == '~' || ch == '.') {
                result.append(ch);
            } else if (ch == '/') {
                result.append(encodeSlash ? "%2F" : ch);
            } else {
//                result.append(toHexUTF8(ch));
                result.append("%").append(Integer.toHexString(ch).toUpperCase());//字符转16进制
            }
        }
        return result.toString();
    }

    /**
     * 调用HMAC SHA256算法，根据开发者提供的密钥（key）和密文（message）输出密文摘要，并把结果转换为小写形式的十六进制字符串。
     * @param signingKey
     * @param stringToSign
     * @return
     */
    public static String HMAC_SHA256_HEX(String signingKey, String stringToSign) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(signingKey.getBytes(Charset.forName("UTF-8")), "HmacSHA256"));
            String str = new String(Hex.encodeHex(mac.doFinal(stringToSign.getBytes( Charset.forName("UTF-8") ) ) ) );
            return str.toLowerCase();
        } catch (Exception e) {
            throw new BceClientException("Fail to generate the signature", e);
        }
    }

    /**
     * 将汉字转换为 utf-8
     * @param s
     * @return
     */
    public static String convertStringToUTF8(String s) {
        if (s == null || s.equals("")) {
            return null;
        }
        StringBuffer sb = new StringBuffer();
        try {
            char c;
            for (int i = 0; i < s.length(); i++) {
                c = s.charAt(i);
                if (c >= 0 && c <= 255) {
                    sb.append(c);
                } else {
                    byte[] b;

                    b = Character.toString(c).getBytes("utf-8");

                    for (int j = 0; j < b.length; j++) {
                        int k = b[j];
                        if (k < 0)
                            k += 256;
                        sb.append(Integer.toHexString(k).toUpperCase());
                        // sb.append("%" +Integer.toHexString(k).toUpperCase());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();

        }
        return sb.toString();
    }

}
